package com.sh.data.engine.application.service.normalization;

import cn.hutool.core.convert.Convert;
import com.sh.data.engine.application.model.dto.base.PagerResponse;
import com.sh.data.engine.application.model.dto.normalization.modeldesign.DictionaryResponse;
import com.sh.data.engine.application.model.dto.normalization.modeldesign.ModelListResponse;
import com.sh.data.engine.application.model.dto.normalization.modeldesign.ModelResponse;
import com.sh.data.engine.application.model.dto.normalization.modeldesign.StorageResponse;
import com.sh.data.engine.application.model.dto.normalization.research.ThemeResponse;
import com.sh.data.engine.application.model.mapper.normalization.ModelListResponsePageMapper;
import com.sh.data.engine.application.model.mapper.normalization.ThemeResponseMapper;
import com.sh.data.engine.application.model.request.normalization.DeleteModelRequest;
import com.sh.data.engine.application.model.request.normalization.SaveModelRequest;
import com.sh.data.engine.application.model.request.normalization.SaveNewModelRequest;
import com.sh.data.engine.application.model.request.normalization.SaveThemeRequest;
import com.sh.data.engine.common.enumDefinition.DSType;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.dataasset.service.TblPropService;
import com.sh.data.engine.domain.normalization.modeldesign.cdm.service.TblRelationService;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelListDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.StorageDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.enums.ModelTypeEnum;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.param.SaveModelParam;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.ModelService;
import com.sh.data.engine.domain.normalization.research.themedesign.model.domain.ThemeDomain;
import com.sh.data.engine.domain.normalization.research.themedesign.model.param.ThemeParam;
import com.sh.data.engine.domain.normalization.research.themedesign.service.ThemeService;
import com.sh.data.engine.domain.normalization.standard.model.domain.DictionaryDomain;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.repository.dao.normalization.modeldesign.cdm.entity.TblRelationEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author: mengzheng.mhc
 * @date: 2024/7/3 16:40
 */
@Slf4j
@Service
public class ModelApplicationService {

    @Autowired
    private ModelService modelService;

    @Autowired
    private TblRelationService tblRelationService;

    @Autowired
    private ThemeService themeService;

    @Autowired
    private TblPropService tblPropService;

    @Autowired
    private ThemeResponseMapper themeResponseMapper;

    @Autowired
    private ModelListResponsePageMapper modelListResponsePageMapper;

    public List<ThemeResponse> treeThemes() {
        Long projectId = ServletUtils.getProjectId();
        List<ThemeDomain> themeDomains = themeService.getThemesByProjectId(projectId);
        return themeResponseMapper.map(themeDomains);
    }

    public boolean saveTheme(SaveThemeRequest themeRequest) {
        Long projectId = ServletUtils.getProjectId();
        themeService.saveTheme(ConvertUtil.copyProperties(themeRequest, ThemeParam.class), projectId);
        return true;
    }

    public StorageResponse getStorageType() {
        Long projectId = ServletUtils.getProjectId();
        StorageDomain storageDomain = modelService.getStorageType(projectId);
        List<StorageResponse.FieldType> fieldTypes =
            ConvertUtil.copyProperties(storageDomain.getFieldTypes(), StorageResponse.FieldType.class);
        StorageResponse storageResponse = new StorageResponse();
        storageResponse.setType(storageDomain.getType());
        storageResponse.setFieldTypes(fieldTypes);
        return storageResponse;
    }

    public List<DictionaryResponse> getDictionaryList() {
        Long projectId = ServletUtils.getProjectId();
        List<DictionaryDomain> dictionaryList =
            modelService.getDictionaryList(projectId);
        return ConvertUtil.copyProperties(dictionaryList, DictionaryResponse.class);
    }

    public List<String> getSchemas() {
        Long projectId = ServletUtils.getProjectId();
        return modelService.getSchemas(projectId);
    }

    public List<ThemeResponse> getThemeList(String modelType) {
        Long projectId = ServletUtils.getProjectId();
        List<ThemeDomain> themeDomains = modelService.getThemesByProjectId(projectId, modelType);
        return ConvertUtil.copyProperties(themeDomains, ThemeResponse.class);
    }

    public List<ThemeResponse> getThemeMenus(String modelType) {
        Long projectId = ServletUtils.getProjectId();
        List<ThemeDomain> themeDomains = modelService.getMenuByProjectId(projectId, modelType);
        return ConvertUtil.copyProperties(themeDomains, ThemeResponse.class);
    }

    public PagerResponse<ModelListResponse> getModelList(
        Integer pageNum, Integer pageSize, Optional<Long> themeId, String modelType, String search) {
        // 转换成类型对应的小分类, 例如modelType是公共模型,转换成dwd, dws, dim
        List<String> nameByType = ModelTypeEnum.getNameByType(modelType);
        PageResult<ModelListDomain> modelList = new PageResult<>();
        // 默认进入页面时不选择主题
        if (!themeId.isPresent()) {
            modelList = modelService.getTablePage(pageNum, pageSize, nameByType, search);
        } else {
            modelList =
                modelService.getTablePageByThemeId(pageNum, pageSize, themeId.get(), nameByType, search);
        }
        Long projectId = ServletUtils.getProjectId();
        List<ModelListDomain> result = modelList.getResult();
        result.forEach(e -> {
            e.setSecretLevel(tblPropService.getSecretLevel(e.getEnName(), projectId));
        });
        PageResult<ModelListResponse> modelListResponsePageResult =
            ConvertUtil.convertPageInfo(modelList, ModelListResponse.class);
        return modelListResponsePageMapper.toPageResponse(modelListResponsePageResult);
    }

    public List<ModelListResponse> getAllModelList(Long themeId, String modelType, String search) {
        // 转换成类型对应的小分类, 例如modelType是公共模型,转换成dwd, dws, dim
        List<String> nameByType = ModelTypeEnum.getNameByType(modelType);
        List<ModelListDomain> tableList = modelService.getTableList(nameByType, search, themeId);
        return Convert.toList(ModelListResponse.class, tableList);
    }

    public PagerResponse<ModelListResponse> getCDMList(Integer pageNum, Integer pageSize, Optional<Long> themeId, String search) {
        Long projectId = ServletUtils.getProjectId();
        PagerResponse<ModelListResponse> cdm = this.getModelList(pageNum, pageSize, themeId, "cdm", search);
        List<ModelListResponse> resultList = cdm.getResult();
        List<Long> tblIds = resultList.stream().map(r -> r.getTblId()).collect(Collectors.toList());
        List<TblRelationEntity> tblRelations = tblRelationService.listTblRelation(projectId, tblIds);
        Set<Long> relationIds = new HashSet<>();
        tblRelations.stream()
            .forEach(
                tr -> {
                    relationIds.add(tr.getMainTableId());
                    relationIds.add(tr.getForeignTableId());
                });
        resultList.forEach(
            re -> {
                if (relationIds.contains(re.getTblId())) {
                    re.setHasRelation(Boolean.TRUE);
                }
            });
        return cdm;
    }

    public ModelResponse getModelByTblId(Long tblId) {
        Long projectId = ServletUtils.getProjectId();
        ModelDomain modelById = modelService.getModelIdByTblId(projectId, tblId);
        return ConvertUtil.copyProperties(modelById, ModelResponse.class);
    }

    public ModelResponse getModelByModelId(Long modelId) {
        if (null == modelId) {
            throw new BusinessException("检查接口传参");
        }
        Long projectId = ServletUtils.getProjectId();
        ModelDomain modelById = modelService.getModelById(projectId, modelId);
        List<ModelResponse.Field> fields =
            ConvertUtil.copyProperties(modelById.getFields(), ModelResponse.Field.class);
        ModelResponse modelResponse = ConvertUtil.copyProperties(modelById, ModelResponse.class);
        modelResponse.setFields(fields);
        return modelResponse;
    }

    public ModelResponse getModelByName(String schema, String tableName) {
        Long projectId = ServletUtils.getProjectId();
        ModelDomain modelById = modelService.getModelByName(projectId, schema, tableName);
        List<ModelResponse.Field> fields =
            ConvertUtil.copyProperties(modelById.getFields(), ModelResponse.Field.class);
        ModelResponse modelResponse = ConvertUtil.copyProperties(modelById, ModelResponse.class);
        modelResponse.setFields(fields);
        return modelResponse;
    }

    public boolean deleteModel(DeleteModelRequest deleteModelRequest) {
        return modelService.deleteModelById(deleteModelRequest.getModelId());
    }

    public boolean saveNewModel(SaveNewModelRequest saveNewModelRequest) {
        Long projectId = ServletUtils.getProjectId();
        StorageDomain storageDomain = modelService.getStorageType(projectId);
        if (null == storageDomain) {
            throw new BusinessException("检查存储配置是否有错误");
        }
        if (DSType.Sap == DSType.valueOf(storageDomain.getType())) {
            // sap need schema
            if (StringUtils.isEmpty(saveNewModelRequest.getSchema())) {
                throw new BusinessException("sap require schema");
            }
        } else {
            if (StringUtils.isNotEmpty(saveNewModelRequest.getSchema())) {
                throw new BusinessException("error request parameter: schema");
            }
        }

        List<SaveNewModelRequest.Field> fields = saveNewModelRequest.getFields();
        List<SaveModelParam.Field> paramFields =
            ConvertUtil.copyProperties(fields, SaveModelParam.Field.class);
        SaveModelParam saveModelParam =
            ConvertUtil.copyProperties(saveNewModelRequest, SaveModelParam.class);
        saveModelParam.setFields(paramFields);
        saveModelParam.setProjectId(projectId);
        modelService.saveNewModel(saveModelParam);
        return true;
    }

    public boolean saveModel(SaveModelRequest saveModelRequest) {
        List<SaveModelParam.Field> paramFields =
            ConvertUtil.copyProperties(saveModelRequest.getFields(), SaveModelParam.Field.class);
        SaveModelParam saveModelParam =
            ConvertUtil.copyProperties(saveModelRequest, SaveModelParam.class);
        saveModelParam.setFields(paramFields);
        modelService.saveModel(saveModelParam);
        return true;
    }

    public List<ModelListResponse> searchTable(String tableName) {
        final List<ModelListDomain> domains = modelService.searchTable(tableName);
        return ConvertUtil.copyProperties(domains, ModelListResponse.class);
    }
}
